package com.yunxi.service.dubboService.impl.otd;

import com.alibaba.dubbo.rpc.RpcContext;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.github.pagehelper.PageInfo;
import com.yunxi.bizToBaseData.service.Biz2BaseVehicleConfigDubboService;
import com.yunxi.core.util.DateUtil;
import com.yunxi.core.util.StringUtil;
import com.yunxi.core.web.ApiResult;
import com.yunxi.core.web.DictConstant;
import com.yunxi.core.web.LoginInfo;
import com.yunxi.core.web.ResponseUtil;
import com.yunxi.model.otd.OtdModelProduceConfig;
import com.yunxi.model.otd.OtdOrderSo;
import com.yunxi.model.otd.OtdProduceCapacityAssignDTO;
import com.yunxi.model.otd.OtdProduceCapacityDTO;
import com.yunxi.model.otd.OtdWeekCalendar;
import com.yunxi.model.otd.OtdWeekPlan;
import com.yunxi.model.otd.OtdWeekPlanDetail;
import com.yunxi.model.otd.OtdWeekPlanSingle;
import com.yunxi.model.otd.VehicleConfigConstraint;
import com.yunxi.otd.pojo.OtdWeekImportVO;
import com.yunxi.otd.pojo.OtdWeekPlanDetailVO;
import com.yunxi.otd.pojo.OtdWeekPlanImportDetailVO;
import com.yunxi.otd.pojo.OtdWeekPlanSingleVO;
import com.yunxi.otd.pojo.OtdWeekPlanVO;
import com.yunxi.otd.service.OtdWeekPlanDubboService;
import com.yunxi.service.localService.CodeCreateService;
import com.yunxi.service.localService.VehicleProductViewService;
import com.yunxi.service.localService.otd.OtdIfProducePlanService;
import com.yunxi.service.localService.otd.OtdModelProduceConfigService;
import com.yunxi.service.localService.otd.OtdOrderSoService;
import com.yunxi.service.localService.otd.OtdProduceCalendarService;
import com.yunxi.service.localService.otd.OtdProduceCapacityService;
import com.yunxi.service.localService.otd.OtdWeekCalendarService;
import com.yunxi.service.localService.otd.OtdWeekPlanDetailService;
import com.yunxi.service.localService.otd.OtdWeekPlanService;
import com.yunxi.service.localService.otd.OtdWeekPlanSingleService;
import com.yunxi.service.localService.otd.VehicleConfigConstraintService;
import com.yunxi.vehicle.pojo.VehicleProductViewVO;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

/**
 * <p>
 * 周度需求计划 服务实现类 weekPlan.state 1新建 2提交 3下发 4驳回 upload state 1未释放 2生产待审核 3驳回 4已下发
 * </p>
 *
 * @author XUQI
 * @since 2019-05-09
 */
//@Service(version = "${demo.service.version}",
//    application = "${dubbo.application.id}",
//    protocol = "${dubbo.protocol.id}",
//    registry = "${dubbo.registry.id}"
//)
public class OtdWeekPlanDubboServiceImpl implements OtdWeekPlanDubboService {

    private static final Logger logger = LoggerFactory.getLogger(OtdWeekPlanDubboServiceImpl.class);

    @Autowired
    @Qualifier(value = "otdWeekPlanServiceImpl")
    OtdWeekPlanService otdWeekPlanService;

    @Autowired
    @Qualifier(value = "otdWeekPlanDetailServiceImpl")
    OtdWeekPlanDetailService otdWeekPlanDetailService;

    @Autowired
    @Qualifier(value = "otdWeekPlanSingleServiceImpl")
    OtdWeekPlanSingleService otdWeekPlanSingleService;

    @Autowired
    @Qualifier(value = "otdWeekCalendarServiceImpl")
    OtdWeekCalendarService otdWeekCalendarService;

    @Autowired
    @Qualifier("codeCreateServiceImpl")
    CodeCreateService codeCreateServiceImpl;

    @Autowired
    @Qualifier("otdIfProducePlanServiceImpl")
    OtdIfProducePlanService producePlanService;

    @Autowired
    @Qualifier("vehicleProductViewServiceImpl")
    VehicleProductViewService productService;

    @Autowired
    @Qualifier("otdProduceCalendarServiceImpl")
    OtdProduceCalendarService produceCalendarService;

    @Autowired
    @Qualifier("otdOrderSoServiceImpl")
    OtdOrderSoService soService;

    @Autowired
    @Qualifier("vehicleConfigConstraintServiceImpl")
    VehicleConfigConstraintService configConstraintService;

    @Autowired
    Biz2BaseVehicleConfigDubboService biz2baseVehicleConfigDubboService;

    @Autowired
    @Qualifier("otdModelProduceConfigServiceImpl")
    OtdModelProduceConfigService modelProduceConfigService;

    @Autowired
    @Qualifier("otdProduceCapacityServiceImpl")
    OtdProduceCapacityService capacityService;

    /**
     * 分页查询周计划上传记录
     *
     * @param weekPlanVO VO
     * @param pageNum 页数
     * @param pageSize 行数
     * @return PageInfo 分页对象
     */
    @Override
    public PageInfo page(OtdWeekPlanVO weekPlanVO, Integer pageNum, Integer pageSize) {
        return otdWeekPlanService.pageByVo(weekPlanVO, pageNum, pageSize);
    }

    /**
     * 周计划导入
     *
     * @param weekImportVO weekImportVO
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult importWeekPlan(OtdWeekImportVO weekImportVO) {
        ////// 约束条件
        Map<String, List<VehicleConfigConstraint>> constraintsMap = configConstraintService
            .findEnableConfigConstraintMap();
//        if (CollectionUtils.isEmpty(constraintsDB)) {
//            throw new RuntimeException("未维护约束关系");
//        }

        ////// 全量工程配置
        // 全量工程配置 MAP,<key:特征值,value:特征族>
        Map<String, String> allConfigMap = biz2baseVehicleConfigDubboService.loadAllEngineeringConfig();

        ////// 投产日约束
        List<OtdModelProduceConfig> modelProduceConfigs = modelProduceConfigService.queryEnableList();

        //W0年月周
        String year = weekImportVO.getYear();
        String week = weekImportVO.getWeek().length() == 1 ? "0" + weekImportVO.getWeek() : weekImportVO.getWeek();
        LambdaQueryWrapper<OtdWeekCalendar> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(OtdWeekCalendar::getYear, year)
            .eq(OtdWeekCalendar::getWeek, week);
        if (otdWeekCalendarService.count(queryWrapper) == 0) {
            return ResponseUtil.fail(-1, "未找到" + year + "年" + week + "周");
        }
        //// 新增
        //查询W1~W5周日历`
        List<OtdWeekCalendar> fivesCalendar = otdWeekCalendarService.findFivesCalendarByWeek(year, week);
        if (fivesCalendar.size() != 5) {
            return ResponseUtil.fail(-1, "请将周生产日历维护至未来5周");
        }

        ////新增周计划
        // Map<key:周数,value:周主表>
        Map<Integer, OtdWeekPlan> weekPlanMap = new HashMap<>();
        for (int i = 0; i < 5; i++) {
            OtdWeekCalendar calendar = fivesCalendar.get(i);
            OtdWeekPlan weekPlan = new OtdWeekPlan();
            weekPlan.setWeekPlanCode(codeCreateServiceImpl.createCode("WP", 5));
            weekPlan.setYear(calendar.getYear());
            weekPlan.setMonth(calendar.getMonth());
            weekPlan.setWeek(calendar.getWeek());
            weekPlan.setState("1");// 新建
            weekPlanMap.put(i + 1, weekPlan);
        }
        int w1Sum = 0, w2Sum = 0, w3Sum = 0, w4Sum = 0, w5Sum = 0;
        //新增周计划明细
        for (OtdWeekPlanImportDetailVO importDetailVO : weekImportVO.getWeekPlanImportVOList()) {
            // 后台对选装包排序
            String customPackCode = importDetailVO.getCustomPackCode();
            // 选装包校验
            if (!StringUtils.isEmpty(customPackCode)) {
                // 是否正确、是否存在互斥
                List<String> packages = Arrays.asList(customPackCode.split("/"));
                boolean check = configConstraintService
                    .checkExcludeCustomPackCode(packages, importDetailVO.getCatalogCode());
                if (!check) {
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return ResponseUtil.fail(-1, "选装包存在互斥：" + customPackCode);
                }
                customPackCode = StringUtil.customPackCodeSort(customPackCode.replace("/", ","));
            } else {
                customPackCode = null;
            }
            String finalCustomPackCode = customPackCode;

            // 查询产品CODE
            VehicleProductViewVO product = productService
                .findProductBySub(importDetailVO.getCatalogCode(), importDetailVO.getColorInCode(),
                    importDetailVO.getColorOutCode());
            if (product == null) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ResponseUtil.fail(-1, "未找到产品信息");
            }

            ////// 约束条件
            List<VehicleConfigConstraint> constraintsDB = constraintsMap.get(product.getModelCode());

            // 投产日校验
            boolean isProduce = checkModelProduceDate(importDetailVO, constraintsDB, allConfigMap, fivesCalendar,
                modelProduceConfigs);
            if (!isProduce) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ResponseUtil.fail(-1,
                    "配置：" + importDetailVO.getCatalogCode() + "，内饰：" + importDetailVO.getColorInCode() + "，外饰："
                        + importDetailVO.getColorOutCode() + "，选装：" + importDetailVO.getCustomPackCode() + " 未到投产日！");
            }

            Map<Integer, Integer> numMap = new HashMap<>();
            numMap.put(1, importDetailVO.getW1Num());
            numMap.put(2, importDetailVO.getW2Num());
            numMap.put(3, importDetailVO.getW3Num());
            numMap.put(4, importDetailVO.getW4Num());
            numMap.put(5, importDetailVO.getW5Num());
            w1Sum += importDetailVO.getW1Num();
            w2Sum += importDetailVO.getW2Num();
            w3Sum += importDetailVO.getW3Num();
            w4Sum += importDetailVO.getW4Num();
            w5Sum += importDetailVO.getW5Num();

            // 创建W1~W5周销售预测明细
            numMap.forEach((key, value) -> createDetail(product.getProductCode(), finalCustomPackCode, value,
                weekPlanMap.get(key).getWeekPlanCode()));
        }

        // 周计划总数
        Map<Integer, Integer> weekMap = new HashMap<>();
        weekMap.put(1, w1Sum);
        weekMap.put(2, w2Sum);
        weekMap.put(3, w3Sum);
        weekMap.put(4, w4Sum);
        weekMap.put(5, w5Sum);

        try {
            weekMap.forEach((key, value) -> {
                if (value > 0) {
                    // 设置周计划汇总数量
                    weekPlanMap.get(key).setSumPlanNum(value);
                    // 根据年周判断是否已制作，如状态不为带释放则删除
                    removeWeekPlan(weekPlanMap.get(key).getYear(), weekPlanMap.get(key).getWeek());
                    // 保存周计划
                    otdWeekPlanService.save(weekPlanMap.get(key));
                }
            });
        } catch (RuntimeException e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseUtil.fail(-1, e.getMessage());
        }
        return ResponseUtil.ok();
    }

    /**
     * 根据上传记录CODE查询周计划
     *
     * @param weekPlanId 周计划ID
     * @return OtdWeekPlanVO 周计划
     */
    @Override
    public OtdWeekPlanVO findWeekPlanVoListByCode(String weekPlanId) {
        OtdWeekPlanVO weekPlanVO = otdWeekPlanService.findWeekPlanById(weekPlanId);
        //判断查询的是W第几周
        Integer weekNum = otdWeekPlanService
            .findWeekNumByWeek(weekPlanVO.getYear(), weekPlanVO.getMonth(), weekPlanVO.getWeek());
        if (weekNum == null || weekNum <= 2) {
            //小于等W2周显示单车
            // 根据ID查询单车
            weekPlanVO.setSingleVOList(otdWeekPlanSingleService.findPlanListByCode(weekPlanVO.getWeekPlanCode()));
        } else {
            // 根据ID查询明细
            weekPlanVO.setWeekPlanDetailVOList(otdWeekPlanDetailService.findDetailList(weekPlanVO.getWeekPlanCode()));
        }
        return weekPlanVO;
    }

    /**
     * 修改单车
     *
     * @param singleVO 周计划单车
     */
    @Override
    public ApiResult modifyPlan(OtdWeekPlanSingleVO singleVO, LoginInfo info) {
        OtdWeekPlanSingle single = new OtdWeekPlanSingle();
        BeanUtils.copyProperties(singleVO, single);
        single.setUpdateBy(info.getUserCode());
        if (!otdWeekPlanSingleService.updateById(single)) {
            return ResponseUtil.fail(-1, "数据过期");
        }
        return ResponseUtil.ok();
    }

    /**
     * 删除周销售预测单车
     *
     * @param singleId 周销售预测单车ID
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult removeSingle(String singleId, LoginInfo info) {
        OtdWeekPlanSingle single = otdWeekPlanSingleService.getById(singleId);
        String weekPlanCode = single.getWeekPlanCode();
        String weekDetailCode = single.getWeekPlanDetailCode();
        otdWeekPlanSingleService.removeById(single.getWeekPlanSingleId());
        // 删除后，判断是否最后一条单车，是则需要删除明细
        Integer planNum = otdWeekPlanSingleService.findPlanCountByDetailCode(weekDetailCode);
        if (planNum == 0) {
            //删除明细
            otdWeekPlanDetailService.removeDetailByCode(weekDetailCode);
        } else {
            // 更新明细数量
            otdWeekPlanDetailService.updateDetailSumNum(weekDetailCode, info.getUserCode());
        }
        // 更新周计划总数量
        otdWeekPlanService.updateWeekPlanNum(info.getUserCode(), weekPlanCode);
        return ResponseUtil.ok();
    }

    /**
     * 新增生产计划单车
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult addSingle(OtdWeekPlanSingleVO singleVO, LoginInfo info) {
        //VO to BEAN
        OtdWeekPlanSingle single = new OtdWeekPlanSingle();
        BeanUtils.copyProperties(singleVO, single);

        String productCode = productService
            .findProductCodeBySub(singleVO.getProduct().getCatalogCode(), singleVO.getProduct().getColorCodeIn(),
                singleVO.getProduct().getColorCodeOut());

        if (StringUtils.isEmpty(productCode)) {
            return ResponseUtil.fail(-1, "请输入正确的产品信息！");
        }
        single.setProductCode(productCode);
        //该车型是否新增的第一条单车，是则需要增加明细
        OtdWeekPlanDetail detailDB = otdWeekPlanDetailService
            .findDetail(singleVO.getWeekPlanCode(), singleVO.getProductCode(), singleVO.getCustomPackCode());
        if (detailDB == null) {
            //新增明细
            OtdWeekPlanDetail detail = new OtdWeekPlanDetail();
            detail.setProductCode(productCode);
            detail.setCustomPackCode(singleVO.getCustomPackCode());
            detail.setWeekPlanCode(singleVO.getWeekPlanCode());
            detail.setWeekPlanDetailCode(codeCreateServiceImpl.createCode("WPD", 5));
            detail.setPlanNum(1);
            //明细CODE
            single.setWeekPlanDetailCode(detail.getWeekPlanDetailCode());
            //保存明细
            detail.setCreateBy(info.getUserCode());
            detail.setUpdateBy(info.getUserCode());
            otdWeekPlanDetailService.save(detail);
        } else {
            single.setWeekPlanDetailCode(detailDB.getWeekPlanDetailCode());
            // 更新明细数量
            detailDB.setPlanNum(detailDB.getPlanNum() + 1);
            detailDB.setUpdateBy(info.getUserCode());
            otdWeekPlanDetailService.updateById(detailDB);
        }
        single.setCreateBy(info.getUserCode());
        single.setUpdateBy(info.getUserCode());
        otdWeekPlanSingleService.save(single);
        // 更新周计划总数量
        otdWeekPlanService.updateWeekPlanNum(info.getUserCode(), singleVO.getWeekPlanCode());
        return ResponseUtil.ok();
    }

    /**
     * 修改明细数量
     *
     * @param detailVO 明细VO
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult modifyDetail(OtdWeekPlanDetailVO detailVO, LoginInfo info) {
        OtdWeekPlanDetail detailDB = otdWeekPlanDetailService.getById(detailVO.getWeekPlanDetailId());
        // 新数量-原数量
        int diffNum = detailVO.getPlanNum() - detailDB.getPlanNum();
        if (diffNum < 0) {
            // 数量减少，删除多余的生产计划单车
            otdWeekPlanSingleService.retainSingleByNum(detailVO.getPlanNum(), detailVO.getWeekPlanDetailCode());
        } else {
            List<OtdWeekPlanSingle> singleList = new ArrayList<>();
            // 数量增加，新增生产计划单车
            for (int i = 0; i < diffNum; i++) {
                OtdWeekPlanSingle single = new OtdWeekPlanSingle();
                single.setWeekPlanDetailCode(detailVO.getWeekPlanDetailCode());
                single.setWeekPlanCode(detailDB.getWeekPlanCode());
                single.setProductCode(detailVO.getProductCode());
                single.setCustomPackCode(detailVO.getCustomPackCode());
                single.setPriorityLevel("3");
                single.setCreateBy(info.getUserCode());
                single.setUpdateBy(info.getUserCode());
                singleList.add(single);
            }
            if (!CollectionUtils.isEmpty(singleList)) {
                otdWeekPlanSingleService.saveBatch(singleList);
            }
        }
        detailDB.setPlanNum(detailVO.getPlanNum());
        // 修改明细
        detailDB.setUpdateBy(info.getUserCode());
        if (!otdWeekPlanDetailService.updateById(detailDB)) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseUtil.fail(-1, "数据过期");
        }
        // 更新周生产计划总数量
        otdWeekPlanService.updateWeekPlanNum(info.getUserCode(), detailDB.getWeekPlanCode());
        return ResponseUtil.ok();
    }

    /**
     * 删除周计划明细
     *
     * @param detailId 删除周计划明细ID
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult removeDetail(String detailId, LoginInfo info) {
        OtdWeekPlanDetail detailDB = otdWeekPlanDetailService.getById(detailId);
        //删除单车
        otdWeekPlanSingleService.removePlanByDetailCode(detailDB.getWeekPlanDetailCode());
        //删除明细
        if (!otdWeekPlanDetailService.removeById(detailDB.getWeekPlanDetailId())) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseUtil.fail(-1, "删除失败");
        }
        // 更新周计划总数量
        otdWeekPlanService.updateWeekPlanNum(info.getUserCode(), detailDB.getWeekPlanCode());
        return ResponseUtil.ok();
    }

    /**
     * 新增周计划明细
     *
     * @param detailVO 周计划明细
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult addDetail(OtdWeekPlanDetailVO detailVO, LoginInfo info) {
        String productCode = productService
            .findProductCodeBySub(detailVO.getProduct().getCatalogCode(), detailVO.getProduct().getColorCodeIn(),
                detailVO.getProduct().getColorCodeOut());
        if (StringUtils.isEmpty(productCode)) {
            return ResponseUtil.fail(-1, "请输入正确的产品信息！");
        }

        OtdWeekPlanDetail detailDB = otdWeekPlanDetailService
            .findDetail(detailVO.getWeekPlanCode(), productCode, detailVO.getCustomPackCode());
        if (detailDB != null) {
            return ResponseUtil.fail(-1, "不能添加相同的产品！");
        }

        //明细VO转BEAN
        OtdWeekPlanDetail detail = new OtdWeekPlanDetail();
        BeanUtils.copyProperties(detailVO, detail);
        detail.setWeekPlanDetailCode(codeCreateServiceImpl.createCode("WPD", 5));
        detail.setProductCode(productCode);

        //新增生产计划单车
        for (int i = 0; i < detail.getPlanNum(); i++) {
            OtdWeekPlanSingle single = new OtdWeekPlanSingle();
            single.setWeekPlanDetailCode(detail.getWeekPlanDetailCode());
            single.setWeekPlanCode(detailVO.getWeekPlanCode());
            single.setProductCode(detail.getProductCode());
            single.setCustomPackCode(detail.getCustomPackCode());
            single.setCreateBy(info.getUserCode());
            single.setUpdateBy(info.getUserCode());
            otdWeekPlanSingleService.save(single);
        }

        // 新增周计划明细
        detail.setCreateBy(info.getUserCode());
        detail.setUpdateBy(info.getUserCode());
        otdWeekPlanDetailService.save(detail);

        // 更新周计划总数量
        otdWeekPlanService.updateWeekPlanNum(info.getUserCode(), detailVO.getWeekPlanCode());
        return ResponseUtil.ok();
    }

    /**
     * 销售释放周计划：添加接口表待发送数据
     *
     * @param weekPlanId 周计划ID
     */
    @LcnTransaction
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult releaseWeek(String weekPlanId) {
        //查询销售预测计划
        OtdWeekPlan weekPlan = otdWeekPlanService.getById(weekPlanId);
        if (!"1".equals(weekPlan.getState())) {
            return ResponseUtil.fail(-1, "该周已无法释放！");
        }

        //周日历
        OtdWeekCalendar calendar = otdWeekCalendarService.findCalendarByWeek(weekPlan.getYear(), weekPlan.getWeek());
        if (calendar == null) {
            return ResponseUtil
                .fail(-1, "请维护" + weekPlan.getYear() + "年" + weekPlan.getWeek()
                    + "周工作日历！");
        }

        //查询明细
        List<OtdWeekPlanDetailVO> detailVOList = otdWeekPlanDetailService.findDetailList(weekPlan.getWeekPlanCode());
        if (CollectionUtils.isEmpty(detailVOList)) {
            return ResponseUtil.fail(-1, "该周没有明细记录");
        }

        //////
        //// 根据产能新增SO订单
        //////
        try {
            //// 根据产能新增SO订单
            //1.有产能条件计算
            //2.无产能条件计算
            calcProductCapacity(calendar, detailVOList);
        } catch (Exception e) {
            e.printStackTrace();
            logger.debug(e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseUtil.fail(-1, e.getMessage());
        }
        // 已释放
        weekPlan.setState("2");
        weekPlan.setReleaseTime(new Date());
        if (!otdWeekPlanService.updateById(weekPlan)) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseUtil.fail(-1, "数据过期");
        }
        return ResponseUtil.ok();
    }


    /**
     * 根据产能新增SO订单
     *
     * @param calendar 周日历
     * @param detailList 周明细
     */
    private void calcProductCapacity(OtdWeekCalendar calendar, List<OtdWeekPlanDetailVO> detailList) {
        // 需求订单分配DTO
        List<OtdProduceCapacityAssignDTO> capacityAssignDTOS = new ArrayList<>();
        detailList.forEach(detailVO -> {
            OtdProduceCapacityAssignDTO capacityAssignDTO = new OtdProduceCapacityAssignDTO();
            capacityAssignDTO.setId(detailVO.getWeekPlanDetailId());
            capacityAssignDTO.setCountry(detailVO.getProduct().getCountry());
            capacityAssignDTO.setModelCode(detailVO.getProduct().getModelCode());
            capacityAssignDTO.setProductCode(detailVO.getProductCode());
            capacityAssignDTO.setColorInCode(detailVO.getProduct().getColorCodeIn());
            capacityAssignDTO.setColorOutCode(detailVO.getProduct().getColorCodeOut());
            capacityAssignDTO.setCustomPackCode(detailVO.getCustomPackCode());
            capacityAssignDTO.setDemandNum(detailVO.getPlanNum());
            capacityAssignDTO.setVehicleCode(detailVO.getProduct().getCatalogCode());
            capacityAssignDTO.setIsLimit(0);
            capacityAssignDTOS.add(capacityAssignDTO);
        });

        // 根据日历查询工作日产能条件
        Map<Date, Map<String, List<OtdProduceCapacityDTO>>> capacityMap = capacityService
            .selectWorkDayCapacity(calendar.getStartDate(), calendar.getEndDate());
        //// 1.有条件限制车型分配
        if (!CollectionUtils.isEmpty(capacityMap)) {
            assignByExpressionProduct(capacityMap, capacityAssignDTOS);
        }

        //// 2.无资源限制车型
        // key:条件 value:List<对应车型+选装>
        List<OtdProduceCapacityAssignDTO> notExpressionList = new ArrayList<>();
        capacityAssignDTOS.forEach(assignDTO -> {
            if (assignDTO.getIsLimit() == 0) {
                notExpressionList.add(assignDTO);
            }
        });
        if (!CollectionUtils.isEmpty(notExpressionList)) {
            assignByNotExpressionProduct(calendar, notExpressionList);
        }

        // 判断是否已分配完成，如有未分配，说明产能不足
        for (OtdProduceCapacityAssignDTO assignDTO : capacityAssignDTOS) {
            if (assignDTO.getDemandNum() > 0) {
                throw new RuntimeException(
                    "配置：" + assignDTO.getVehicleCode() + "内饰色：" + assignDTO.getColorInCode() + "外饰色：" + assignDTO
                        .getColorOutCode() + "可生产数量不足");
            }
        }
    }

    /**
     * 有条件限制车型分配
     *
     * @param capacityMap 产能条件
     * @param capacityAssignDTOS 需求车型分配DTO
     */
    private void assignByExpressionProduct(Map<Date, Map<String, List<OtdProduceCapacityDTO>>> capacityMap,
        List<OtdProduceCapacityAssignDTO> capacityAssignDTOS) {
        String currentUserStr = RpcContext.getContext().getAttachment("currentUser");
        ///////////////// 按条件计算产能
        // 车型每天每个工厂的最大可生产数量 Map<日期, Map<工厂, Map<车型, 最大产能数量>>>
        Map<Date, Map<String, Map<String, Integer>>> modelWorksDayMaxCapacity = new HashMap<>();
        //////// 有资源限制车型 Map<需求日期,Map<条件,需求车型>>
        Map<Date, Map<String, List<OtdProduceCapacityAssignDTO>>> expressionMap = getExpressionProductMap(capacityMap,
            capacityAssignDTOS, modelWorksDayMaxCapacity);
        RpcContext.getContext().setAttachment("currentUser", currentUserStr);
        if (CollectionUtils.isEmpty(expressionMap)) {
            return;
        }

        List<OtdOrderSo> orderSos = new ArrayList<>();
        /////按日期遍历分配数量
        for (Entry<Date, Map<String, List<OtdProduceCapacityAssignDTO>>> expressionEntry : expressionMap.entrySet()) {
            // 需求日期
            Date day = expressionEntry.getKey();
            logger.debug("开始分配日期" + DateUtil.format(day));
            // 该需求日期对应的需要分配车型的按条件分组Map
            Map<String, List<OtdProduceCapacityAssignDTO>> assignExpression = expressionEntry.getValue();
            // 该需求日期对应的条件产能
            Map<String, List<OtdProduceCapacityDTO>> dayCapacity = capacityMap.get(day);
            // 已分配产能数<ID,已分配数量>
//            Map<String, Integer> assignedMap = new HashMap<>();

            // 工厂已分配产能数 <工厂,<ID,已分配数量>>
            Map<String, Map<String, Integer>> worksAssignedMap = new HashMap<>();

            // 该分配日期下每个车型每个工厂的最大可生产数量
            // Map<工厂, Map<车型, 最大产能数量>>
            Map<String, Map<String, Integer>> modelWorksMaxCapacity = modelWorksDayMaxCapacity.get(day);

            //// 遍历分配每个条件下的需求车型
            for (Entry<String, List<OtdProduceCapacityAssignDTO>> entry : assignExpression.entrySet()) {
                // expression：条件
                String expression = entry.getKey();
                logger.debug("条件：" + expression);
                // assignDTOS：需求车型分配对象
                List<OtdProduceCapacityAssignDTO> assignDTOS = entry.getValue();
                // 该需求日期条件对应产能(多工厂)
                List<OtdProduceCapacityDTO> capacityDTOs = dayCapacity.get(expression);
                for (OtdProduceCapacityDTO produceCapacityDTO : capacityDTOs) {
                    // 已分配数量 <ID,已分配数量>
                    Map<String, Integer> assignedMap = worksAssignedMap.get(produceCapacityDTO.getWorks());
                    if (assignedMap == null) {
                        assignedMap = new HashMap<>();
                    }

                    logger.debug("工厂：" + produceCapacityDTO.getWorks());
                    // 该每个工厂每个车型的最大可生产数量
                    // Map<工厂, Map<车型, 最大产能数量>>
                    Map<String, Integer> modelMaxCapacity = modelWorksMaxCapacity.get(produceCapacityDTO.getWorks());

                    // 可分配产能
                    int capacity = produceCapacityDTO.getCapacity();
                    logger.debug("可分配产能：" + capacity);

                    // 是否分配完成
                    boolean isOver = false;
                    capacity:
                    while (capacity > 0 && !isOver) {
                        isOver = true;
                        // 遍历条件对应所有车型
                        for (OtdProduceCapacityAssignDTO assignDTO : assignDTOS) {
                            logger.debug("分配车型：" + assignDTO.getId());
                            // 需求车型的需求数量
                            int demandNum = assignDTO.getDemandNum();
                            if (demandNum == 0) {
                                logger.debug("需求数量0，已满足");
                                continue;
                            }

                            // 需求车型单天最大能满足数量
                            int demandMaxCapacity = modelMaxCapacity.get(assignDTO.getId());
                            if (demandMaxCapacity == 0) {
                                continue;
                            }
                            // 已达到单天最大能满足数量
                            Integer assignedNum = assignedMap.putIfAbsent(assignDTO.getId(), 0);
                            assignedNum = assignedNum == null ? 0 : assignedNum;
                            if (demandMaxCapacity == assignedNum) {
                                logger.debug("已达到单天最大能满足数量");
                                continue;
                            }
                            // 已分配+1
                            assignedMap.put(assignDTO.getId(), assignedNum + 1);
                            worksAssignedMap.put(produceCapacityDTO.getWorks(), modelMaxCapacity);

                            // 新增需求订单
                            orderSos.add(createOrderSO(assignDTO.getProductCode(), assignDTO.getCustomPackCode(),
                                assignDTO.getCountry(), day, produceCapacityDTO.getWorks()));

                            // 需求数-1
                            demandNum--;
                            assignDTO.setDemandNum(demandNum);
                            logger.debug("需求数量" + demandNum);
                            if (demandNum != 0) {
                                isOver = false;
                            }

                            // 产能-1
                            capacity--;
                            if (capacity == 0) {
                                break capacity;
                            }
                        }
                    }
                }
            }
        }
        soService.saveBatch(orderSos);
    }


    /**
     * 无条件限制车型分配
     *
     * @param calendar 周日历
     * @param notExpressionList 无条件限制车型
     */
    private void assignByNotExpressionProduct(OtdWeekCalendar calendar,
        List<OtdProduceCapacityAssignDTO> notExpressionList) {
        // 根据周度日期查询工作日日期集合
        List<Date> days = produceCalendarService.findWorkDay(calendar.getStartDate(), calendar.getEndDate());
        // 日期下标
        int index = 0;
        List<OtdOrderSo> orderSos = new ArrayList<>();
        for (OtdProduceCapacityAssignDTO assignDTO : notExpressionList) {
            // 需求数量
            int num = assignDTO.getDemandNum();
            for (int i = 0; i < num; i++) {
                // 创建待发送SO订单
                orderSos.add(
                    createOrderSO(assignDTO.getProductCode(), assignDTO.getCustomPackCode(), assignDTO.getCountry(),
                        days.get(index), "AP21"));
                // 日期数组下标++
                index++;
                // 最大日期后从最小日期重新开始
                if (index == days.size()) {
                    index = 0;
                }
            }
            // 设置需求已满足
            assignDTO.setDemandNum(0);
        }
        soService.saveBatch(orderSos);
    }

    /**
     * 创建SO订单
     *
     * @param productCode 产品编码
     * @param customPackCode 选装包编码
     * @param country 国别
     * @param produceDate 分配日期
     */
    private OtdOrderSo createOrderSO(String productCode, String customPackCode, String country, Date produceDate,
        String works) {
        OtdOrderSo soOrder = new OtdOrderSo();
        soOrder.setPlanNo(codeCreateServiceImpl.createCode("P", "yyMMdd", 5));
        soOrder.setProductCode(productCode);
        soOrder.setCustomPackCode(customPackCode);
        soOrder.setCarName(soService.generateCarName(productCode, customPackCode));
        //预计生产日期
        soOrder.setDemandDate(produceDate);
        //接口类型：新增
        soOrder.setInterfaceType("I");
        soOrder.setCarLimitState(0);
        //订单类型
        if ("MSCH".equals(country)) {
            soOrder.setOrderType(DictConstant.OTD_ORDER_TYPE_INVE);
        } else if ("MSEU".equals(country)) {
            soOrder.setOrderType(DictConstant.OTD_ORDER_TYPE_EXVE);
        }
        // 工厂   后期根据产能设置分配工厂
        soOrder.setWorks(works);
        //待发送
        soOrder.setSapState("N");
        return soOrder;
    }

    /**
     * 按需求日期分组条件、按条件分组需求车型
     *
     * @param capacityMap 条件集合
     * @param capacityAssignDTOS 需求对象
     * @param modelWorksDayMaxCapacity 车型每天每个工厂的最大可生产数量 Map<日期, Map<工厂, Map<车型, 最大产能数量>>>
     * @return Map<需求日期, Map < 条件, 需求车型>>
     */
    private Map<Date, Map<String, List<OtdProduceCapacityAssignDTO>>> getExpressionProductMap(
        Map<Date, Map<String, List<OtdProduceCapacityDTO>>> capacityMap,
        List<OtdProduceCapacityAssignDTO> capacityAssignDTOS,
        Map<Date, Map<String, Map<String, Integer>>> modelWorksDayMaxCapacity) {

        ////// 按约束条件分组 Map<需求日期,Map<条件,需求车型>>
        Map<Date, Map<String, List<OtdProduceCapacityAssignDTO>>> capacityProductMap = new HashMap<>();

        /// 全量工程配置
        // 全量工程配置 MAP,<key:特征值,value:特征族>
        Map<String, String> allConfigMap = biz2baseVehicleConfigDubboService.loadAllEngineeringConfig();
        /// 约束条件
        Map<String, List<VehicleConfigConstraint>> constraintsMap = configConstraintService
            .findEnableConfigConstraintMap();

        ///// 遍历分配日期
        for (Entry<Date, Map<String, List<OtdProduceCapacityDTO>>> capacityEntry : capacityMap.entrySet()) {
            // 需求日期
            Date day = capacityEntry.getKey();
            // 日期对应产能
            Map<String, List<OtdProduceCapacityDTO>> capacityDTOMap = capacityEntry.getValue();
            // 产能条件所对应的需求车型
            Map<String, List<OtdProduceCapacityAssignDTO>> conditionProductMap = new HashMap<>();

            // 该分配日期下每个车型每个工厂的最大可生产数量
            // Map<工厂, Map<车型, 最大产能数量>>
            Map<String, Map<String, Integer>> modelWorksMaxCapacity = modelWorksDayMaxCapacity.get(day);
            if (modelWorksMaxCapacity == null) {
                modelWorksMaxCapacity = new HashMap<>();
            }

            // 遍历日期对应产能
            for (Entry<String, List<OtdProduceCapacityDTO>> capacityDTOEntry : capacityDTOMap.entrySet()) {
                // 条件表达式
                String expression = capacityDTOEntry.getKey();
                // 判断是否已有分组，无则新增，有则追加
                List<OtdProduceCapacityAssignDTO> tempList = conditionProductMap.get(expression);
                if (CollectionUtils.isEmpty(tempList)) {
                    tempList = new ArrayList<>();
                }

                // 循环所有需求车型
                for (OtdProduceCapacityAssignDTO assignDTO : capacityAssignDTOS) {
                    /// 单车配置约束条件
                    List<VehicleConfigConstraint> constraintsDB = constraintsMap.get(assignDTO.getModelCode());
                    // 根据约束规则获取单车配置
                    // 单车默认配置MAP,<key:特征族,value:特征值>
                    Map<String, String> configMap = biz2baseVehicleConfigDubboService
                        .loadEngineeringConfigByVehicleCode(assignDTO.getVehicleCode(),
                            assignDTO.getColorInCode(),
                            assignDTO.getColorOutCode(), assignDTO.getCustomPackCode(), allConfigMap);
                    if (!CollectionUtils.isEmpty(constraintsDB)) {
                        configConstraintService.replaceConfig(constraintsDB, configMap, allConfigMap);
                        for (VehicleConfigConstraint constraint : constraintsDB) {
                            constraint.setIsProcess(0);
                        }
                    }
                    // 是否满足条件
                    boolean isCheck = configConstraintService.checkIsMeet(expression, configMap);
                    if (!isCheck) {
                        continue;
                    }
                    // 需求车型标记为有条件限制
                    assignDTO.setIsLimit(1);
                    tempList.add(assignDTO);
                    // 设置需求车型最大最小值
                    // 条件产能设置
                    List<OtdProduceCapacityDTO> capacityDTO = capacityDTOEntry.getValue();
                    // 取最小产能数为需求车型每天最大数量
                    for (OtdProduceCapacityDTO produceCapacityDTO : capacityDTO) {
                        // 该分配日期下每个车型每个工厂的最大可生产数量
                        //  Map<车型, 最大产能数量>
                        Map<String, Integer> modelMaxCapacity = modelWorksMaxCapacity
                            .get(produceCapacityDTO.getWorks());
                        if (modelMaxCapacity == null) {
                            modelMaxCapacity = new HashMap<>();
                        }
                        Integer capacity = modelMaxCapacity.get(assignDTO.getId());
                        if (capacity == null || produceCapacityDTO.getCapacity().compareTo(capacity) < 0) {
                            modelMaxCapacity.put(assignDTO.getId(), produceCapacityDTO.getCapacity());
                            modelWorksMaxCapacity.put(produceCapacityDTO.getWorks(), modelMaxCapacity);
                        }
                    }
                }
                if (!CollectionUtils.isEmpty(tempList)) {
                    conditionProductMap.put(expression, tempList);
                }
            }
            if (!CollectionUtils.isEmpty(conditionProductMap)) {
                capacityProductMap.put(day, conditionProductMap);
            }
            modelWorksDayMaxCapacity.put(day, modelWorksMaxCapacity);
        }
        return capacityProductMap;
    }

    /**
     * 获取车型对应订单类型
     *
     * @param detailList 周计划明细
     * @return Map<产品Code, 订单类型>
     */
    private Map<String, String> getOrderTypeMap(List<OtdWeekPlanDetailVO> detailList) {
        Map<String, String> orderTypeMap = new HashMap<>();
        detailList.forEach(detailVO -> {
            String orderType = DictConstant.OTD_ORDER_TYPE_INVE;
            if ("MSCH".equals(detailVO.getProduct().getCountry())) {
                orderType = DictConstant.OTD_ORDER_TYPE_INVE;
            } else if ("MSEU".equals(detailVO.getProduct().getCountry())) {
                orderType = DictConstant.OTD_ORDER_TYPE_EXVE;
            }
            orderTypeMap.put(detailVO.getProductCode() + "," + (detailVO.getCustomPackCode() == null ? "NULL"
                : detailVO.getCustomPackCode()), orderType);
        });
        return orderTypeMap;
    }

    /**
     * 根据年周判断是否已制作，如状态不为带释放则删除
     *
     * @param year 年
     * @param week 周
     */
    private void removeWeekPlan(String year, String week) {
        //查询是否已制作
        OtdWeekPlan weekPlanDB = otdWeekPlanService.findWeekPlanByWeek(year, week);
        if (weekPlanDB != null) {
            if (!weekPlanDB.getState().equals("1")) {
                throw new RuntimeException("已无法重新制作" + year + "年" + week + "周销售预测！");
            } else {
                // 删除原有的周计划、周明细、周单车
                otdWeekPlanService.removeWeekPlanByCode(weekPlanDB.getWeekPlanCode());
                otdWeekPlanDetailService.removeDetailByWeekPlanCode(weekPlanDB.getWeekPlanCode());
                otdWeekPlanSingleService.removeSingleByWeekPlanCode(weekPlanDB.getWeekPlanCode());
            }
        }
    }

    /**
     * 创建周度销售预测明细&单车
     */
    private void createDetail(String productCode, String customPackCode, Integer planNum, String weekPlanCode) {
        if (planNum == 0) {
            return;
        }
        OtdWeekPlanDetail detail = new OtdWeekPlanDetail();
        detail.setProductCode(productCode);
        detail.setCustomPackCode(customPackCode);
        detail.setWeekPlanCode(weekPlanCode);
        detail.setWeekPlanDetailCode(codeCreateServiceImpl.createCode("WPD", 5));
        detail.setPlanNum(planNum);
        otdWeekPlanDetailService.save(detail);
        //创建单车
        createSingle(detail, weekPlanCode);
    }

    /**
     * 创建W1周销售预测计划单车
     *
     * @param detail 需求明细
     * @param weekPlanCode codeCreateServiceImpl
     */
    private void createSingle(OtdWeekPlanDetail detail, String weekPlanCode) {
        for (int i = 1; i <= detail.getPlanNum(); i++) {
            //新增生产计划
            OtdWeekPlanSingle single = new OtdWeekPlanSingle();
            single.setPriorityLevel("3");
            single.setProductCode(detail.getProductCode());
            single.setCustomPackCode(detail.getCustomPackCode());
            single.setWeekPlanCode(weekPlanCode);
            single.setWeekPlanDetailCode(detail.getWeekPlanDetailCode());
            otdWeekPlanSingleService.save(single);
        }
    }

    /**
     * 校验车型投产时间
     *
     * @param importDetailVO 导入数据
     * @param constraintsDB 约束关系
     * @param allConfigMap 全量工程配置
     * @param fivesCalendar 下5周
     * @param modelProduceConfigs 车型投产日配置
     * @return 是否可以投产
     */
    private boolean checkModelProduceDate(OtdWeekPlanImportDetailVO importDetailVO,
        List<VehicleConfigConstraint> constraintsDB, Map<String, String> allConfigMap,
        List<OtdWeekCalendar> fivesCalendar, List<OtdModelProduceConfig> modelProduceConfigs) {

        // 根据约束规则获取单车配置
        // 单车默认配置MAP,<key:特征族,value:特征值>
        Map<String, String> configMap = biz2baseVehicleConfigDubboService
            .loadEngineeringConfigByVehicleCode(importDetailVO.getCatalogCode(), importDetailVO.getColorInCode(),
                importDetailVO.getColorOutCode(), importDetailVO.getCustomPackCode(), allConfigMap);
        if (!CollectionUtils.isEmpty(constraintsDB)) {
            configConstraintService.replaceConfig(constraintsDB, configMap, allConfigMap);
            for (VehicleConfigConstraint constraint : constraintsDB) {
                constraint.setIsProcess(0);
            }
        }

        if (CollectionUtils.isEmpty(modelProduceConfigs)) {
            return true;
        }
        // 遍历投产日设置
        for (OtdModelProduceConfig modelProduceConfig : modelProduceConfigs) {
            // 是否满足条件，满足则校验投产日
            boolean isCheck = configConstraintService.checkIsMeet(modelProduceConfig.getRuleExpression(), configMap);
            if (isCheck) {
                // 根据单车配置校验是否达到投产日
                Date startDate = null; // 周开始时间
                if (importDetailVO.getW1Num() > 0) {
                    startDate = fivesCalendar.get(0).getStartDate();
                } else if (importDetailVO.getW2Num() > 0) {
                    startDate = fivesCalendar.get(1).getStartDate();
                } else if (importDetailVO.getW3Num() > 0) {
                    startDate = fivesCalendar.get(2).getStartDate();
                } else if (importDetailVO.getW4Num() > 0) {
                    startDate = fivesCalendar.get(3).getStartDate();
                } else if (importDetailVO.getW5Num() > 0) {
                    startDate = fivesCalendar.get(4).getStartDate();
                }
                // 最小需求日期小于投产日则不能投产
                if (startDate != null && startDate.before(modelProduceConfig.getProduceDate())) {
                    return false;
                }
            }
        }
        return true;
    }
}
